﻿using Microsoft.WindowsAzure.ServiceRuntime;
using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace CloudTrace
{
    [CLSCompliant(false)]
    [Serializable()]
    public class LogMessage
    {
        public DateTime Timestamp { get; set; }

        public string Message { get; set; }
        public string InstanceId { get; set; }
        public string Category { get; set; }

        [NonSerialized()]
        private int indentLevel;
        [NonSerialized()]
        internal TraceOptions traceOutputOptions;
        [NonSerialized()]
        internal TraceEventCache eventCache;
        internal TraceEventType traceEventType;
        [NonSerialized()]
        private static readonly string instanceId;
        [NonSerialized()]
        protected static readonly string deploymentId;

        #region header
        public string Source { get; set; }
        public string TraceEventType { get { return Enum.GetName(typeof(TraceEventType), traceEventType); } set { if (!string.IsNullOrEmpty(value)) traceEventType = (TraceEventType)Enum.Parse(typeof(TraceEventType), value); } }
        public int EventId { get; set; }
        #endregion

        #region footer
        public int ProcessId { get; set; }
        public string LogicalOperationStack { get; set; }
        public string ThreadId { get; set; }
        public string Callstack { get; set; }
        #endregion

        static LogMessage()
        {
            if (RoleEnvironment.IsAvailable)
            {
                instanceId = RoleEnvironment.CurrentRoleInstance.Id;
                deploymentId = RoleEnvironment.DeploymentId;
            }
            else
            {
                instanceId = AppDomain.CurrentDomain.FriendlyName;
                deploymentId = Guid.Empty.ToString();
            }
        }

        public LogMessage() { }

        public LogMessage(string message, string category)
        {
            this.Message = message;
            this.Category = category;
            this.Timestamp = DateTime.UtcNow;
            this.InstanceId = instanceId;
        }

        public LogMessage(string source, TraceEventType eventType, int id, TraceEventCache eventCache, TraceOptions options, string message)
        {
            this.Message = message;
            this.Source = source;
            this.traceEventType = eventType;
            this.EventId = id;

            this.eventCache = eventCache;
            this.traceOutputOptions = options;

            this.Timestamp = (eventCache != null) ? eventCache.DateTime : DateTime.UtcNow;
            this.InstanceId = instanceId;

        }

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            if (!string.IsNullOrEmpty(Source))
            {
                builder.AppendFormat(CultureInfo.InvariantCulture, "{0} {1}: {2} : ",
                    new object[] { this.Source, this.TraceEventType, this.EventId.ToString(CultureInfo.InvariantCulture) });
            }

            builder.AppendLine(Message);

            if (this.traceOutputOptions != TraceOptions.None)
            {
                if (!string.IsNullOrEmpty(Category))
                    builder.AppendLine("Category=" + Category);

                if (!string.IsNullOrEmpty(deploymentId))
                    builder.AppendLine("DeploymentId=" + deploymentId);
                if (!string.IsNullOrEmpty(InstanceId))
                    builder.AppendLine("InstanceId=" + InstanceId);
            }

            if (eventCache != null)
            {
                this.indentLevel++;
                if (this.IsEnabled(TraceOptions.ProcessId))
                {
                    builder.AppendLine("ProcessId=" + eventCache.ProcessId);
                }
                if (this.IsEnabled(TraceOptions.LogicalOperationStack))
                {
                    builder.Append("LogicalOperationStack=");
                    Stack logicalOperationStack = eventCache.LogicalOperationStack;
                    bool flag = true;
                    foreach (object stackItem in logicalOperationStack)
                    {
                        if (!flag)
                        {
                            builder.Append(", ");
                        }
                        else
                        {
                            flag = false;
                        }
                        builder.Append(stackItem.ToString());
                    }
                    builder.AppendLine(string.Empty);
                }
                if (this.IsEnabled(TraceOptions.ThreadId))
                {
                    builder.AppendLine("ThreadId=" + eventCache.ThreadId);
                }
                if (this.IsEnabled(TraceOptions.DateTime))
                {
                    builder.AppendLine("DateTime=" + eventCache.DateTime.ToString("o", CultureInfo.InvariantCulture));
                }
                if (this.IsEnabled(TraceOptions.Timestamp))
                {
                    builder.AppendLine("Timestamp=" + eventCache.Timestamp);
                }
                if (this.IsEnabled(TraceOptions.Callstack))
                {
                    builder.AppendLine("Callstack=" + eventCache.Callstack);
                }
                this.indentLevel--;
            }
            return builder.ToString();
        }

        private bool IsEnabled(TraceOptions opts)
        {
            return ((opts & this.traceOutputOptions) != TraceOptions.None);
        }

        public void SetProperties()
        {
            if (eventCache != null)
            {
                if (this.IsEnabled(TraceOptions.ProcessId))
                {
                    this.ProcessId = eventCache.ProcessId;
                }
                if (this.IsEnabled(TraceOptions.LogicalOperationStack))
                {
                    StringBuilder builder = new StringBuilder();
                    Stack logicalOperationStack = eventCache.LogicalOperationStack;
                    bool flag = true;
                    foreach (object stackItem in logicalOperationStack)
                    {
                        if (!flag)
                        {
                            builder.Append(", ");
                        }
                        else
                        {
                            flag = false;
                        }
                        builder.Append(stackItem.ToString());
                    }
                    this.LogicalOperationStack = builder.ToString();
                }
                if (this.IsEnabled(TraceOptions.ThreadId))
                {
                    this.ThreadId = eventCache.ThreadId;
                }
                //if (this.IsEnabled(TraceOptions.Timestamp))
                //{
                //    builder.AppendLine("Timestamp=" + eventCache.Timestamp);
                //}
                if (this.IsEnabled(TraceOptions.Callstack))
                {
                    this.Callstack = eventCache.Callstack;
                }
            }
        }

        #region Serializable
        public static byte[] Serialize(LogMessage message)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, message);
                return stream.ToArray();
            }
        }

        public static LogMessage Deserialize(byte[] buffer)
        {
            using (MemoryStream stream = new MemoryStream(buffer))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                object o = formatter.Deserialize(stream);
                return (LogMessage)o;
            }
        }
        #endregion

    }
}

